package org.springblade.core.etl.dao;

import lombok.extern.slf4j.Slf4j;
import org.springblade.core.ds.SQResult;
import org.springblade.core.ds.model.DataSourceConfig;
import org.springblade.core.ds.model.DataSourceLog;
import org.springblade.core.tool.utils.FnKit;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据相关操作
 */
@Slf4j
public class EtlDao {

    /**
     * 取得数据库连接
     *
     * @return
     * @throws Exception
     */
    private static Connection getConnection(DataSourceConfig dbConfig) throws Exception {
        Connection connection = ClientHelper.getConn(dbConfig);
        if (connection == null) {
            ClientHelper.clearConnection(dbConfig);
        }
        return connection;
    }

    /**
     * 构建 PreparedStatement
     *
     * @return
     * @throws Exception
     */
    public static void run(SQResult sqlRes, DataSourceConfig config) throws Exception {
        PreparedStatement ps = null;
        try {
            //取得连接
            Connection connection = EtlDao.getConnection(config);
            if (connection == null) {
                log.error("执行时取得数据库连接失败,地址:{}", config.getUrl());
                return;
            }
            if (sqlRes.getSql() == null || sqlRes.getSql().length() == 0) {
                return;
            }
            //取得执行对象
            ps = connection.prepareStatement(sqlRes.getSql());
            List<List<Object>> valueLists = sqlRes.getValueList();
            if (valueLists == null) {
                valueLists = new ArrayList<>();
            }
            if (valueLists.size() > 1) {
                int batchSize = 2000;
                List<List<List<Object>>> batchValueList = FnKit.split(valueLists, batchSize);
                for (List<List<Object>> batch : batchValueList) {
                    StringBuilder logParam = new StringBuilder();
                    for (List<Object> valueList : batch) {
                        logParam.append(assemblyStatementParam(ps, valueList));
                        ps.addBatch();
                    }
                    if (DataSourceLog.isDebug()) {
                        log.debug("SQL:\n{}\n{}", sqlRes.getSql().trim(), logParam.toString());
                    }
                    ps.executeBatch();
                    if (DataSourceLog.isInfo()) {
                        log.info("批量执行,  个数: {}", batch.size());
                    }
                }
            } else {
                if (DataSourceLog.isDebug()) {
                    if (valueLists.size() > 0) {
                        log.debug("SQL:\n{}\n{}\n", sqlRes.getSql().trim(), assemblyStatementParam(ps, valueLists.get(0)));
                    } else {
                        log.debug("SQL:\n{}\n", sqlRes.getSql().trim());
                    }
                }
                ps.execute();
            }
        } catch (Exception ex) {
            log.error("执行异常" + ex.getMessage(), ex);
            log.error("清理连接完成");
            ClientHelper.clearConnection(config);
            throw ex;
        } finally {
            if (ps != null) {
                try {
                    ps.close();
                } catch (Exception ex) {
                }
            }
        }
    }


    /**
     * 查询
     *
     * @return
     * @throws Exception
     */
    public static List<Map<String, Object>> list(SQResult sqlRes, DataSourceConfig config) throws Exception {
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //取得连接
            Connection connection = EtlDao.getConnection(config);
            if (connection == null) {
                log.error("查询时取得数据库连接失败,地址:{}", config.getUrl());
                return new ArrayList<>();
            }
            if (sqlRes.getSql() == null || sqlRes.getSql().length() == 0) {
                return new ArrayList<>();
            }
            //取得执行对象
            ps = connection.prepareStatement(sqlRes.getSql());
            StringBuilder logParam = new StringBuilder();
            List<List<Object>> valueLists = sqlRes.getValueList();
            if (valueLists == null) {
                valueLists = new ArrayList<>();
            }
            if (valueLists.size() > 0) {
                logParam.append(assemblyStatementParam(ps, sqlRes.getValueList().get(0)));
            }
            if (DataSourceLog.isDebug()) {
                if (valueLists.size() > 0) {
                    log.debug("SQL:\n{}\n{}\n", sqlRes.getSql().trim(), logParam.toString());
                } else {
                    log.debug("SQL:\n{}\n", sqlRes.getSql().trim());
                }

            }
            rs = ps.executeQuery();
            List<Map<String, Object>> lineDataList = parseResult(rs);
            return lineDataList;
        } catch (Exception ex) {
            ClientHelper.clearConnection(config);
            throw ex;
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (Exception ex) {
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (Exception ex) {
                }
            }
        }
    }


    /**
     * 绑定参数
     *
     * @param ps
     * @param valueList
     * @throws SQLException
     */
    private static StringBuilder assemblyStatementParam(PreparedStatement ps, List<Object> valueList) throws SQLException {
        StringBuilder logParam = new StringBuilder();
        logParam.append("(");
        for (int i = 0; i < valueList.size(); i++) {
            //PreparedStatement
            ps.setObject(i + 1, valueList.get(i));
            if (i > 0) {
                logParam.append(",");
            }
            logParam.append(valueList.get(i));
        }
        logParam.append(")\n");
        return logParam;
    }


    /**
     * 处理数据库查询结果
     *
     * @param rs
     * @return
     * @throws SQLException
     */
    private static List<Map<String, Object>> parseResult(ResultSet rs) throws SQLException {
        ResultSetMetaData rsmd = rs.getMetaData();
        int count = rsmd.getColumnCount();
        List<String> columns = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            columns.add(rsmd.getColumnName(i + 1));
        }
        List<Map<String, Object>> lineDataList = new ArrayList<>();
        Map<String, Object> lineData;
        String column;
        while (rs.next()) {
            lineData = new HashMap<>();
            for (int i = 0; i < columns.size(); i++) {
                column = columns.get(i);
                lineData.put(column, rs.getObject(column));
            }
            lineDataList.add(lineData);
        }
        return lineDataList;
    }
}
